<script setup>
import { Transport, ESPLoader } from 'esptool-js';
import { downloadFirmware } from '@/api/firmware.js'
import { Status, useSerialStore } from '@/stores/serial'
import { ref } from 'vue'
import { Delete } from '@element-plus/icons-vue'

const store = useSerialStore()

const SerialLines = ref([]);

const espTerminal = {
  buffer: '',
  flushTimer: null,
  FLUSH_TIMEOUT_MS: 300, // 超时时间，可以根据实际调试调整
  clean() {
    SerialLines.value = [];
    this.buffer = '';
    this.clearFlushTimer();
  },
  writeLine(data) {
    this.insertLine(data);
  },
  write(data) {
    this.buffer += data;
    let lines = this.buffer.split(/[\r\n]+/);
    for (let i = 0; i < lines.length - 1; i++) {
      this.writeLine(lines[i]);
    }
    this.buffer = lines[lines.length - 1];
    this.resetFlushTimer();
  },
  insertLine(line) {
    console.info(line);
    const time = new Date();
    const timestamp = `[${time.toLocaleTimeString('en-GB')}]`; // 输出为 HH:MM:SS
    const lineWithTime = `${timestamp}  ${line}`;
    SerialLines.value.push(lineWithTime);
  },
  flushBuffer() {
    if (this.buffer.trim() !== '') {
      this.writeLine(this.buffer);
      this.buffer = '';
    }
    this.clearFlushTimer();
  },
  resetFlushTimer() {
    this.clearFlushTimer();
    this.flushTimer = setTimeout(() => this.flushBuffer(), this.FLUSH_TIMEOUT_MS);
  },
  clearFlushTimer() {
    if (this.flushTimer) {
      clearTimeout(this.flushTimer);
      this.flushTimer = null;
    }
  }
}


let device = null;
let transport = null;
let esploader = null;
let monitorPort = null;
let monitorReader = null;
const filters = [
  { usbVendorId: 0x303A }, // Espressif 原生 USB
  { usbVendorId: 0x10C4 }, // CP210x
  { usbVendorId: 0x1A86 }, // CH340
  { usbVendorId: 0x0403 }  // FTDI
];

const url = "/hello_world_c3_v0.0.1.bin";

navigator.serial.addEventListener("connect", (e) => {
  // Connect to `e.target` or add it to a list of available ports.
});

navigator.serial.addEventListener("disconnect", (e) => {
  // Remove `e.target` from the list of available ports.
});

async function handleConnect() {
  device = await navigator.serial.requestPort({ filters });
  transport = new Transport(device, false);
  const flashOptions = {
      transport,
      baudrate: 115200,
      debugLogging: false,
      trace: false,
      verbose: false,
      terminal: espTerminal,
  };
  esploader = new ESPLoader(flashOptions);
  const chip = await esploader.main();
  console.log('Connected to', chip);
  store.setConnectStatus(Status.CONNECTED);
}

async function handleDisconnect() {
  console.log("当前状态:", store.connectStatus);

  try {
    if (store.connectStatus === Status.CONNECTING || store.connectStatus === Status.CONNECTED) {
      if (transport?.disconnect) {
        await transport.disconnect(); // ⚠️ 记得 await
        console.info("Transport disconnected.");
      }
      transport = null;
      esploader = null;
      device = null;
    }

    if (store.connectStatus === Status.MONITORING) {
      try {
        if (monitorReader) {
          try {
            await monitorReader.cancel();
          } catch (e) {
            console.warn("monitorReader.cancel() 失败:", e);
          }

          try {
            await monitorReader.releaseLock();
          } catch (e) {
            console.warn("monitorReader.releaseLock() 失败:", e);
          }

          monitorReader = null;
        }

        if (monitorPort) {
          await monitorPort.close();
          monitorPort = null;
        }
      } catch (e) {
        console.error("关闭 monitor 串口失败:", e);
      }
    }
  } catch (e) {
    console.error("断开串口失败:", e);
  }

  store.setConnectStatus(Status.DISCONNECTED);
  console.log("断开完成");
}

async function erase() {
  console.info('erase started');
  await esploader.eraseFlash();
  console.info('erash ended');
}

async function flash(){
  console.info('flash started');
  const firmwareDATA = await downloadFirmware(url);

  console.log(typeof firmwareDATA)

   await esploader.writeFlash({
     flashSize: '4MB',
     compress: true,
     fileArray: [{ address: 0x0, data: firmwareDATA }],
     flashMode: 'DIO',
     flashFreq: 40,
     reportProgress: (fileIndex, written, total) => {
           const percent = ((written / total) * 100).toFixed(1);
           console.info(`Flashing file ${fileIndex}: ${percent}% (${written}/${total} bytes)`);
         }
  });
  console.info('flash ended');
}

async function log(){
  monitorPort = await navigator.serial.requestPort({ filters });
  await monitorPort.open({ baudRate: 115200 });
  monitorReader = monitorPort.readable.getReader();
  const decoder = new TextDecoder();

  store.setConnectStatus(Status.MONITORING);
  console.info('Program monitor started…');
  try {
    while (true) {
      const { value, done } = await monitorReader.read();
      if (done) break;
      const text = decoder.decode(value);
      espTerminal.write(text);
    }
  } catch (e) {
    console.warn('Monitor read error:', e);
  } finally {
    await monitorReader?.releaseLock();
    monitorReader = null;
  }
}
</script>

<template>
  <div class="main">
    <div class="description">
      <div class="text">
        <span class="title1">ESP32 串口通信</span>
        <span class="title2">支持esp32c3, esp32c6, esp32s3, esp8266</span>
      </div>
    </div>

    <div class="content">
      <div class="actions">
        <el-button type="primary" @click="handleConnect" v-if="store.connectStatus === Status.DISCONNECTED">连接设备</el-button>
        <el-button type="primary" @click="handleDisconnect" v-if="store.connectStatus === Status.CONNECTED || store.connectStatus === Status.MONITORING">断开设备</el-button>
        <el-button type="primary" @click="erase" v-if="store.connectStatus === Status.CONNECTED">擦除设备</el-button>
        <el-button type="primary" @click="flash" v-if="store.connectStatus === Status.CONNECTED">烧录设备</el-button>
        <el-button type="primary" @click="log" v-if="store.connectStatus === Status.DISCONNECTED">设备日志</el-button>
        <el-button type="primary" @click="log" v-if="store.connectStatus === Status.CONNECTED || store.connectStatus === Status.MONITORING">重启设备</el-button>
      </div>

      <div class="title">
        <span class="title1">接受数据</span>
        <el-button class="clear" @click="() => espTerminal.clean()" type="" text :icon="Delete">
          清除日志
        </el-button>
      </div>

      <div class="data">
        <div v-for="(line, index) in SerialLines" :key="index">
          <div>{{line}}</div>
        </div>
        <div class="placeholder" v-if="SerialLines.length === 0">等待接收数据...</div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.main {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  border-radius: 8px;
}

.title1 {
  color:#111827;
  font-size: 18px;
  line-height: 28px;
}

.description {
  display: flex;
  width: 100%;
  height: 164px;
  align-items: center;

  .text {
    display: flex;
    flex-direction: column;
    height: 68px;
  }

  .title1 {
    color: #111827;
    font-size: 30px;
    line-height: 36px;
    margin-bottom: 12px;
  }

  .title2 {
    color: #4B5563;
    font-size: 16px;
    line-height: 24px;
  }
}

.content {
  width: 100%;
  background-color: #fff;
  height: calc(100% - 164px);
  padding: 32px;
  display: flex;
  flex-direction: column;

  .actions {
    width: 100%;
    height: 80px;
  }

  .title {
    width: 100%;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    height: 48px;

    .clear {
      font-size: 16px;
      line-height: 24px;
      color: #4B5563;
      position: relative;
      top: 16px;
      left: 16px;
    }
  }

  .data {
    width: 100%;
    height: calc(100% - 128px);
    background-color: #F9FAFB;
    border: 1px solid #E5E7EB;
    position: relative;
    overflow: auto;
    padding: 16px;

    .placeholder {
      display: flex;
      font-size: 16px;
      color: #6B7280;
      line-height: 24px;
      position: absolute;
      left: 16px;
      top: 16px;
    }
  }
}
</style>
